#include <signal.h>
#include <stdlib.h>
#include "Mem.h"
#include "ExceptionManager.h"
#include "List.h"

static ListVirtualTable* ListVirtualTableInstance;

List* ListNew(unsigned long int pArgsCount, va_list* pParameters)
{
    List* this;
    if(!(this = (List*) malloc(sizeof(List))))
    {
        raise(SIGABRT);
    }
    this->VirtualTable = ListInitialize(this, pArgsCount, pParameters);
    this->Constructor = ListConstructor;
    this->Destructor = ListDestructor;
    this->Clone = ListClone;
    this->Add = ListAdd;
    this->Insert = ListInsert;
    this->Remove = ListRemove;
    this->Items = ListItems;
    this->IsInList = ListIsInList;
    this->Count = ListCount;
    this->Constructor(this, pArgsCount, pParameters);
    return this;
}

ListVirtualTable* ListInitialize(List* this, unsigned long int pArgsCount, va_list* pParameters)
{
    if(!ListVirtualTableInstance)
    {
        ListVirtualTableInstance = (ListVirtualTable*) malloc(sizeof(ListVirtualTable));
        if(!ListVirtualTableInstance)
        {
            raise(SIGABRT); /* this = 0 => error*/
        }
        ListVirtualTableInstance->Interface = 0;

        ListVirtualTableInstance->TypeID = LIST_TYPEID;

        ListVirtualTableInstance->AncestorTable = ClassInitialize((Class*)this, pArgsCount, pParameters);

        ListVirtualTableInstance->ReferenceCount = 0;

        ListVirtualTableInstance->new = ListNew;
        ListVirtualTableInstance->Initialize = ListInitialize;
        ListVirtualTableInstance->Garbage = (void(*)(List*))ListVirtualTableInstance->AncestorTable->Garbage;
        ListVirtualTableInstance->TypeOf = (unsigned long int (*)(List*))ListVirtualTableInstance->AncestorTable->TypeOf;
        ListVirtualTableInstance->IsSameTypeThanTypeID = (bool (*)(List*, unsigned long int))ListVirtualTableInstance->AncestorTable->IsSameTypeThanTypeID;
        ListVirtualTableInstance->IsKindOfTypeID = (bool (*)(List*, unsigned long int))ListVirtualTableInstance->AncestorTable->IsKindOfTypeID;
        ListVirtualTableInstance->IsSameTypeThan = (bool (*)(List*, Class*))ListVirtualTableInstance->AncestorTable->IsSameTypeThan;
        ListVirtualTableInstance->IsKindOf = (bool (*)(List*, Class*))ListVirtualTableInstance->AncestorTable->IsKindOf;
    }
    return ListVirtualTableInstance;
}

void ListConstructor(List* this, unsigned long int pArgsCount, va_list* pParameters)
{
    this->aItems = 0;
    this->aCount = 0;
}

void ListDestructor(List* this)
{
    while(this->Count(this))
    {
        this->Remove(this, 0);
    }
    free(this->aItems);
    this->aItems = 0;
}

List* ListClone(List* this)
{
    unsigned long int vIndex;
    List* vClone = new((void*(*)(unsigned long int, va_list*))ListNew, 0);

    for(vIndex = 0; vIndex < this->aCount; vIndex++)
    {
        vClone->Add(vClone, this->aItems[vIndex]);
    }
    return vClone;
}

void ListAdd(List* this, Class* pItem)
{
    if(!this->aItems)
    {
        this->aItems = calloc(this->aCount+1, sizeof(Class*));
    }
    else
    {
        this->aItems = (Class**)realloc(this->aItems, ((this->aCount + 1)*sizeof(Class*)));
    }
    this->aItems[this->aCount] = pItem;
    this->aCount++;
}

void ListInsert(List* this, Class* pItem, unsigned long int pIndex)
{
    unsigned long int vIndex;
    if(pIndex > this->aCount - 1)
    {
        this->Add(this, pItem);
        return;
    }
    if(!this->aItems)
    {
        this->aItems = calloc(this->aCount+1, sizeof(Class*));
    }
    else
    {
        this->aItems = (Class**)realloc(this->aItems, (this->aCount + 1) * sizeof(Class*));
    }
    for(vIndex = this->aCount; vIndex > pIndex; vIndex--)
    {
        this->aItems[vIndex] = this->aItems[vIndex - 1];
    }
    this->aItems[pIndex] = pItem;
    this->aCount++;
}

void ListRemove(List* this, unsigned long int pIndex)
{
    unsigned long int vIndex;
    if(pIndex > this->aCount - 1)
    {
        ((ExceptionManager*)new((void*(*)(unsigned long int, va_list*))ExceptionManagerNew,0))->throw(new((void*(*)(unsigned long int, va_list*))ExceptionNew, 1, "List: pIndexParameter in ListRemove method is out of range."));
    }

    if(this->aItems)
    {
        for(vIndex = pIndex; vIndex < this->aCount; vIndex++)
        {
            this->aItems[vIndex] = this->aItems[vIndex + 1];
        }
        this->aItems =(Class**)realloc(this->aItems, (this->aCount - 1) * sizeof(Class*));
        this->aCount++;
    }
}

Class* ListItems(List* this, unsigned long int pIndex)
{
    if(pIndex > this->aCount - 1)
    {
        ((ExceptionManager*)new((void*(*)(unsigned long int, va_list*))ExceptionManagerNew,0))->throw(new((void*(*)(unsigned long int, va_list*))ExceptionNew, 1, "List: pIndexParameter in ListItems is out of range."));
    }
    return this->aItems[pIndex];
}

unsigned long int ListCount(List* this)
{
    return this->aCount;
}

bool ListIsInList(List* this, unsigned long int* pIndex, Class* pItem)
{
    unsigned long int vIndex = 0;
    while(vIndex < this->aCount)
    {
        if(this->aItems[vIndex] == pItem)
        {
            *pIndex = vIndex;
            return true;
        }
        vIndex++;
    }
    return false;
}
